package com.spring.boot;

import com.spring.context.ConfigurableApplicationContext;
import com.spring.core.env.ConfigurableEnvironment;
import com.spring.core.metrics.ApplicationStartup;
import com.spring.core.metrics.StartupStep;
import com.spring.logging.Log;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;

/**
 * @author liyuncong
 * @version 1.0
 * @file SpringApplicationRunListeners
 * @brief SpringApplicationRunListeners
 * @details SpringApplicationRunListeners
 * @date 2022-12-08
 *
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2022-12-08               liyuncong          Created
 */
class SpringApplicationRunListeners {

    private final Log log;

    private final List<SpringApplicationRunListener> listeners;

    private final ApplicationStartup applicationStartup;

    public SpringApplicationRunListeners(
        Log log,
        Collection<? extends SpringApplicationRunListener> listeners,
        ApplicationStartup applicationStartup
    ) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
        this.applicationStartup = applicationStartup;
    }

    void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        doWithListeners("spring.boot.application.starting",
            listener -> listener.starting(bootstrapContext),
            step -> {
                if (null != mainApplicationClass) {
                    step.tag("mainApplication", mainApplicationClass.getName());
                }
            });
    }

    void started(ConfigurableApplicationContext context, Duration timeTaken) {
        doWithListeners("spring.boot.application.started",
            listener -> listener.ready(context, timeTaken));
    }

    void ready(ConfigurableApplicationContext context, Duration timeTaken) {
        doWithListeners("spring.boot.application.ready",
            listener -> listener.ready(context, timeTaken));
    }

    void environmentPrepared(
        ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment
    ) {
        doWithListeners("spring.boot.application.environment-prepared",
            listener -> listener.environmentPrepared(bootstrapContext, environment));
    }

    private void doWithListeners(
        String stepName,
        Consumer<SpringApplicationRunListener> listenerAction
    ) {
        doWithListeners(stepName, listenerAction, null);
    }

    private void doWithListeners(
        String stepName,
        Consumer<SpringApplicationRunListener> listenerAction,
        Consumer<StartupStep> stepAction
    ) {
        StartupStep step = this.applicationStartup.start(stepName);
        this.listeners.forEach(listenerAction);
        if (null != stepAction) {
            stepAction.accept(step);
        }
        step.end();
    }
}
